package com.hyw.encryption.configuration;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.lang.Nullable;

import com.hyw.encryption.advice.DecryptRequestBodyAdvice;
import com.hyw.encryption.advice.EncryptResponseBodyAdvice;
import com.hyw.encryption.provider.IDecryptionProvider;
import com.hyw.encryption.provider.IEncryptionProvider;
import com.hyw.encryption.provider.extractor.IDecryptKeyExtract;
import com.hyw.encryption.provider.extractor.IEncryptKeyExtract;
import com.hyw.encryption.provider.extractor.http.HttpHeaderKeyExtractor;
import com.hyw.encryption.service.IUserKeyStoreService;
import com.hyw.encryption.service.impl.DefaultUserKeyStoreServiceImpl;

/**
 * 用于启动和业务相关的加解器. 该功能在非Debug模式默认开启,
 * <p>
 * <b>可以通过配置hyw.encryption.enable=false,以关闭业务上加解器</b>
 * <p>
 * 但建议更好的方式是通过配置<b>hyw.encryption.skipper</b>的情况下，通过特别的请求头和标记符来跳过框架的加解密。只是会有一个风险就是请求头中的关键信息被外泄。
 * 
 * @author Hongyu
 */
@ConditionalOnProperty(name = "hyw.encryption.enable", havingValue = "true", matchIfMissing = true)
@Configuration
@EnableConfigurationProperties(value = EncryptionSkipperConfig.class)
public class EncryptionAutoConfiguration {

  private static final String ANONY_PREFIX = "Anony";

  /**
   * 配置响应加密
   * 
   * @return
   */
  @Bean
  public EncryptResponseBodyAdvice encryptResponseBodyAdvice(@Autowired List<IEncryptionProvider> encryptors,
      @SuppressWarnings("rawtypes") @Autowired IEncryptKeyExtract encryptKeyExtract,
      @Autowired EncryptionSkipperConfig skipper) {
    return new EncryptResponseBodyAdvice(encryptors, encryptKeyExtract, skipper);
  }

  /**
   * 配置请求解密
   * 
   * @return
   */
  @Bean
  public DecryptRequestBodyAdvice decryptRequestBodyAdvice(@Autowired List<IDecryptionProvider> decryptors,
      @SuppressWarnings("rawtypes") @Autowired IDecryptKeyExtract decryptKeyExtract,
      @Autowired EncryptionSkipperConfig skipper) {
    return new DecryptRequestBodyAdvice(decryptors, decryptKeyExtract, skipper);
  }

  @Bean
  @ConditionalOnMissingBean(value = IUserKeyStoreService.class)
  public IUserKeyStoreService defaultUserKeyStoreService(@Autowired RedisTemplate<String, String> redisTemplate,
      // 默认的游客账户信息为7天，即86400*7=604800
      @Value("${hyw.encryption.anonymous.ttl:604800}") long anonymousTTLInSeconds) {
    return new DefaultUserKeyStoreServiceImpl(redisTemplate, anonymousTTLInSeconds,
        uid -> uid.startsWith(ANONY_PREFIX));
  }

  /**
   * 基于Http请求头获取关键标记的默认实现
   */
  @SuppressWarnings("rawtypes")
  @Bean
  @ConditionalOnMissingBean(value = IEncryptKeyExtract.class)
  public IEncryptKeyExtract encryptKeyExtract() {
    return new HttpHeaderKeyExtractor();
  }

  /**
   * 基于Http请求头获取关键标记的默认实现
   */
  @SuppressWarnings("rawtypes")
  @Bean
  @ConditionalOnMissingBean(value = IDecryptKeyExtract.class)
  public IDecryptKeyExtract decryptKeyExtract() {
    return new HttpHeaderKeyExtractor();
  }

  @Bean
  @ConditionalOnMissingBean(value = IEncryptionProvider.class)
  public IEncryptionProvider silentEncryptAndDecryptProvider() {
    return new SilentEncryptAndDecryptProvider();
  }

  @Bean
  @ConditionalOnMissingBean(value = IDecryptionProvider.class)
  public IDecryptionProvider silentDecryptAndDecryptProvider() {
    return new SilentEncryptAndDecryptProvider();
  }

  /**
   * 一个默认实现, 代表啥也不干
   * 
   * @author Hongyu
   */
  private static class SilentEncryptAndDecryptProvider implements IEncryptionProvider, IDecryptionProvider {

    @Override
    public byte[] encryptByKey(@Nullable String key, byte[] content) {
      return content;
    }

    @Override
    public String getAlgorithm() {
      return "RSA";
    }

    @Override
    public byte[] decryptByKey(@Nullable String key, byte[] content) {
      return content;
    }

  }

}
